package main

import (
	"fmt"
	"time"
	"sync"
	"os"
	"os/signal"
	"syscall"
	"math"
)

const n = 50

var mutexMain sync.Mutex

func main() {
	deferMain(restoreGetByteEcho)

	c := CreateScreenMap()
	snake := CreateSnakeOnMap(c[:][:])
	GAME_START_TIME = time.Now()
	go CreateFoodOnMap(c[:][:])
	go snakeMoveWhileTime(snake)
	snakeMoveWhileMan(snake)
	fmt.Println("GAME OVER", time.Since(GAME_START_TIME))
}

func CreateScreenMap() [][n]int {
	a := new([n][n] int)
	for i := 0; i < n; i++ {
		for j := 0; j < n; j++ {
			if (i == 0 || j == 0 || i == n - 1 || j == n - 1) {
				a[i][j] = mvFence
			}
		}
	}
	return a[:][:]
}

func snakeMoveWhileTime(snake *SnakeType) {
	for !GAME_OVER_BOOL_FLAG {
		mutexMain.Lock()
		direction := snake.Direction()
		snake.Move(direction)
		mutexMain.Unlock()
		showSnakeScreen(snake.aMap)
		time.Sleep(calcDurationSleep(snake))
	}
}

func snakeMoveWhileMan(snake *SnakeType) {
	for !GAME_OVER_BOOL_FLAG {
		a := getch()
		if a == stringESC {
			GAME_OVER_BOOL_FLAG = true
			return
		}
		mutexMain.Lock()
		direction := snake.Direction()
		var m0 PointXY
		if (a != " ") {
			m0 = moveDirection[a]
			if m0.X == 0 && m0.Y == 0 {
				mutexMain.Unlock()
				continue
			}
			if m0.X != 0 && m0.X == -direction.X || m0.Y != 0 && m0.Y == -direction.Y {
				mutexMain.Unlock()
				continue
			}
		} else {
			m0 = *direction
		}
		snake.Move(&m0)
		mutexMain.Unlock()
		showSnakeScreen(snake.aMap)
	}
}

func deferMain(functions ...func()) {
	c := make(chan os.Signal, 2)
	signal.Notify(c, os.Interrupt, syscall.SIGTERM)
	go func() {
		<-c
		for _, f := range (functions) {
			f()
		}
		os.Exit(0)
	}()
}

func calcDurationSleep(snake *SnakeType) time.Duration {
	v0 := 0.5 - float64(snake.sBody.Len()) * 0.005
	vx := math.Max(0.1, v0)
	return time.Duration(float64(time.Second) * float64(vx))
}